package br.senai.sc.controller.business;

import java.io.Serializable;
import java.math.BigInteger;

import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.jboss.seam.international.status.Messages;
import org.jboss.seam.international.status.builder.BundleKey;

import br.senai.sc.model.CorpoHidrico;
import br.senai.sc.model.Foto;
import br.senai.sc.model.PontoColeta;
import br.senai.sc.util.SystemConstants;

/**
 * Contém as regras de negócios relacionadas a entidade {@link PontoColeta}.
 * 
 * @author Tiago Wanke Marques &lt;tiago.marques@sc.senai.br&gt;
 */
public class PontoColetaBusiness implements Serializable {

	private static final long serialVersionUID = 1L;

	@PersistenceContext
	private EntityManager em;

	@Inject
	private Messages messages;

	@Inject
	private CorpoHidricoBusiness corpoHidricoBusiness;

	/**
	 * @param pontoColeta
	 * @return True caso {@link PontoColeta} esteja cadastrado no banco, false
	 *         caso contrário.
	 */
	public boolean existe(PontoColeta pontoColeta) {

		if ((pontoColeta == null) || (pontoColeta.getIdPontoColeta() == null)) {
			return false;
		}

		BigInteger total = (BigInteger) em
				.createNativeQuery(
						"SELECT count(*) FROM ponto_coleta pc WHERE id_ponto_coleta = :idPontoColeta")
				.setParameter("idPontoColeta", pontoColeta.getIdPontoColeta())
				.getSingleResult();

		return total.intValue() > 0;
	}

	/**
	 * @param pontoColeta
	 * @return True caso existe um {@link PontoColeta} cadastrado com o mesmo
	 *         nome e o mesmo {@link CorpoHidrico}.
	 */
	public boolean mesmoNomeMesmoCorpoHidrico(PontoColeta pontoColeta) {

		if ((pontoColeta == null) || (pontoColeta.getNome() == null)) {
			return false;
		}

		CorpoHidrico corpoHidrico = pontoColeta.getCorpoHidrico();
		if ((corpoHidrico == null)
				|| (corpoHidrico.getIdCorpoHidrico() == null)) {
			return false;
		}

		BigInteger total = (BigInteger) em
				.createNativeQuery(
						"SELECT count(*) FROM ponto_coleta pc WHERE nome LIKE :nome AND id_corpo_hidrico = :idCorpoHidrico")
				.setParameter("nome", pontoColeta.getNome())
				.setParameter("idCorpoHidrico",
						corpoHidrico.getIdCorpoHidrico()).getSingleResult();

		return total.intValue() > 0;
	}

	/**
	 * @param pontoColeta
	 * @return True caso existe um {@link PontoColeta} cadastrado com o mesmo
	 *         nome e o mesmo {@link CorpoHidrico}, porém com id diferente.
	 */
	public boolean mesmoNomeMesmoCorpoHidricoIdDiferente(PontoColeta pontoColeta) {

		if ((pontoColeta == null) || (pontoColeta.getNome() == null)
				|| (pontoColeta.getIdPontoColeta() == null)) {
			return false;
		}

		CorpoHidrico corpoHidrico = pontoColeta.getCorpoHidrico();

		if ((corpoHidrico == null)
				|| (corpoHidrico.getIdCorpoHidrico() == null)) {
			return false;
		}

		BigInteger total = (BigInteger) em
				.createNativeQuery(
						"SELECT count(*) FROM ponto_coleta pc WHERE nome LIKE :nome AND id_corpo_hidrico = :idCorpoHidrico AND id_ponto_coleta != :idPontoColeta")
				.setParameter("nome", pontoColeta.getNome())
				.setParameter("idCorpoHidrico",
						corpoHidrico.getIdCorpoHidrico())
				.setParameter("idPontoColeta", pontoColeta.getIdPontoColeta())
				.getSingleResult();

		return total.intValue() > 0;
	}

	/**
	 * @param pontoColeta
	 * @return True caso o {@link PontoColeta} contenha
	 *         {@link br.senai.sc.model.Amostra}, false caso contrário.
	 */
	public boolean contemAmostra(PontoColeta pontoColeta) {

		if ((pontoColeta == null) || (pontoColeta.getIdPontoColeta() == null)) {
			return false;
		}

		BigInteger total = (BigInteger) em
				.createNativeQuery(
						"SELECT count(*) FROM amostra a WHERE id_ponto_coleta = :idPontoColeta")
				.setParameter("idPontoColeta", pontoColeta.getIdPontoColeta())
				.getSingleResult();

		return total.intValue() > 0;
	}

	/**
	 * @return True caso existe {@link PontoColeta} cadastrada no sistema, false
	 *         caso contrário.
	 */
	public boolean existePontoColetaCadastrado() {

		BigInteger total = (BigInteger) em.createNativeQuery(
				"SELECT count(*) FROM ponto_coleta pc").getSingleResult();

		return total.intValue() > 0;
	}

	/**
	 * Executa os métodos necessários para verificar se o {@link PontoColeta}
	 * pode ser salvo. Caso houver alguma inconsistência adiciona ao atributo de
	 * mensagens da classe.
	 * 
	 * @param pontoColeta
	 * @return True caso o {@link PontoColeta} pode ser salvo, false caso
	 *         contrário.
	 */
	public boolean podeSalvar(PontoColeta pontoColeta) {

		CorpoHidrico corpoHidrico = pontoColeta.getCorpoHidrico();

		messages.clear();

		if (!corpoHidricoBusiness.existe(corpoHidrico)) {
			messages.error(new BundleKey(
					SystemConstants.PATH_TO_APPLICATION_MESSAGES,
					"pontosColeta.validacao.corpoHidricoInexistente"));

		} else if (!corpoHidricoBusiness.possuiCidade(corpoHidrico,
				pontoColeta.getCidade())) {
			messages.error(new BundleKey(
					SystemConstants.PATH_TO_APPLICATION_MESSAGES,
					"pontosColeta.validacao.cidadeForaDoCorpoHidrico"));
		}

		if (mesmoNomeMesmoCorpoHidrico(pontoColeta)) {
			messages.error(new BundleKey(
					SystemConstants.PATH_TO_APPLICATION_MESSAGES,
					"pontosColeta.validacao.nomeJaExistenteDentroDoCorpoHidrico"));
		}

		for (Foto foto : pontoColeta.getFotos()) {

			if (AnexoBusiness.possuiTamanhoPermitido(foto.getAnexo())) {
				messages.error(new BundleKey(
						SystemConstants.PATH_TO_APPLICATION_MESSAGES,
						"anexo.validacao.tamanhoPermitido"),
						SystemConstants.MAX_UPLOAD_FILE_SIZE);
				break;
			}

			if (FotoBusiness.altLargPermitidas(foto)) {
				messages.error(new BundleKey(
						SystemConstants.PATH_TO_APPLICATION_MESSAGES,
						"foto.validacao.alturaLarguraPermitida"),
						SystemConstants.IMAGE_MAX_WITH,
						SystemConstants.IMAGE_MAX_HEIGHT);
				break;
			}

			if (!FotoBusiness.tipoPermitido(foto)) {

				messages.error(new BundleKey(
						SystemConstants.PATH_TO_APPLICATION_MESSAGES,
						"foto.validacao.tiposPermitidos"),
						SystemConstants.ALLOWED_IMAGE_MIME_TYPES.toString());
				break;
			}
		}

		return messages.isEmpty();
	}

	/**
	 * Executa os métodos necessários para verificar se {@link PontoColeta} pode
	 * ser editado. Caso houver alguma inconsistência adiciona ao atributo de
	 * mensagens da classe.
	 * 
	 * @param pontoColeta
	 * @return True caso {@link PontoColeta} pode ser editado, false caso
	 *         contrário.
	 */
	public boolean podeEditar(PontoColeta pontoColeta) {

		CorpoHidrico corpoHidrico = pontoColeta.getCorpoHidrico();

		messages.clear();

		if (!existe(pontoColeta)) {
			messages.error(new BundleKey(
					SystemConstants.PATH_TO_APPLICATION_MESSAGES,
					"pontosColeta.validacao.pontoColetaInexistente"));
		}

		if (!corpoHidricoBusiness.existe(corpoHidrico)) {
			messages.error(new BundleKey(
					SystemConstants.PATH_TO_APPLICATION_MESSAGES,
					"pontosColeta.validacao.corpoHidricoInexistente"));

		} else if (!corpoHidricoBusiness.possuiCidade(corpoHidrico,
				pontoColeta.getCidade())) {
			messages.error(new BundleKey(
					SystemConstants.PATH_TO_APPLICATION_MESSAGES,
					"pontosColeta.validacao.cidadeForaDoCorpoHidrico"));
		}

		if (mesmoNomeMesmoCorpoHidricoIdDiferente(pontoColeta)) {
			messages.error(new BundleKey(
					SystemConstants.PATH_TO_APPLICATION_MESSAGES,
					"pontosColeta.validacao.nomeJaExistenteDentroDoCorpoHidrico"));
		}

		return messages.isEmpty();
	}

	/**
	 * Executa os métodos necessários para verificar se {@link PontoColeta} pode
	 * ser removido. Caso houver alguma inconsistência adiciona ao atributo de
	 * mensagens da classe.
	 * 
	 * @param pontoColeta
	 * @return True caso a {@link PontoColeta} pode ser removida, false caso
	 *         contrário.
	 */
	public boolean podeRemover(PontoColeta pontoColeta) {

		messages.clear();

		if (!existe(pontoColeta)) {
			messages.error(new BundleKey(
					SystemConstants.PATH_TO_APPLICATION_MESSAGES,
					"pontosColeta.validacao.pontoColetaInexistente"));
		}

		if (contemAmostra(pontoColeta)) {
			messages.error(new BundleKey(
					SystemConstants.PATH_TO_APPLICATION_MESSAGES,
					"pontosColeta.validacao.removerAmostraUtilizando"));
		}

		return messages.isEmpty();
	}

}
